package VMM;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

import Exceptions.FrameOverflowEx;
import Exceptions.OffsetEx;
import Exceptions.PageOverflowEx;

/**
 * A class to model physical memory. Frame size is the number of bytes each
 * frame holds.
 * 
 * @author allanwatson
 * @changelog 9/2/12 Tidied code and comments. Added methods for retrieving
 *            frame size and number of frames. Added FrameOverflowException to
 *            catch the case when a page is too big to fit in the frame
 * 
 *            16/2/12 Working on code to handle when memory is smaller than
 *            virtual Using a HashMap to map frame numbers to pages Condition
 *            for when memory is full. Victimise method using Fifo. Having
 *            trouble managing the hashtable. Telling me there are 128 items but
 *            only prints 120.
 * 
 *            17/2/12 Moved to using an arrayList and a queue to keep track of
 *            accesses. FIFO
 * 
 *            18/2/12 Code cleanup addition of exceptions. Changed to a LRU
 *            victim policy
 */
public class PhysicalMem {

	private int frameSize, noFrames;
	private ArrayList<byte[]> memory;
	private Queue<Integer> qe; // queue for keeping track of the oldest frame

	public PhysicalMem(int nF, int fS) {
		frameSize = fS;
		noFrames = nF;
		memory = new ArrayList<byte[]>();
		qe = new LinkedList<Integer>();
	}

	/**
	 * Adds a page to a specific frame number, and returns this number.
	 */
	public int addPage(byte[] data) throws FrameOverflowEx {
		if (data.length > frameSize)
			throw new FrameOverflowEx();
		if (memory.size() == noFrames) { // if memory is full
			int vic = victimise();// select a victim frame
			memory.remove(vic);
			memory.add(vic, data);
			qe.add(vic);

			return vic;
		}

		else {// if memory has space...
			memory.add(data.clone());// ...add to memory
			qe.add(memory.size() - 1);// add to used queue

			return memory.size() - 1;
		}
	}

	/**
	 * Selects a victim frame using LRU policy
	 */
	private int victimise() {
		int v = qe.remove();
		return v;

	}

	/**
	 * Returns the value located within a specific frame at a specific offset
	 */
	public int get(int frame, int offset) throws FrameOverflowEx,
			PageOverflowEx, OffsetEx {
		if (frame > frameSize)
			throw new FrameOverflowEx();
		if (offset > frameSize)
			throw new OffsetEx();

		byte[] page = memory.get(frame); // fetch page from memory
		qe.remove(frame);
		qe.add(frame);// move frame to end of queue

		return page[offset]; // return the value at offset
	}

	/**
	 * Returns the size of a frame
	 */
	public int getFrameSize() {
		return this.frameSize;
	}
}
